const GrpcCore = require('grpc');
const protoLoader = require('@grpc/proto-loader');
const path = require('path');
const fs = require('fs');
const nacosClient = require('./nacosClient');

module.exports = async () => {
  const config = saas.config.grpcServer;
  const protoPath = config.protoPath;
  // 创建server
  saas.grpcServer = new GrpcCore.Server();
  await getAllServices(path.join(saas.ROOT_PATH, protoPath), config);
  saas.grpcServer.bind(`${config.host}:${config.port}`, GrpcCore.ServerCredentials.createInsecure());
  saas.grpcServer.start();
  console.log(`[saasjs-grpc-server] grpc-server start on: ${config.host}:${config.port}`);
};


async function getAllServices(protoPath, config) {
  if (!fs.existsSync(protoPath)) {
    throw new Error('[saasjs-grpc-server] no proto file');
  }
  //找到所有的proto文件
  const protoFileList = fs.readdirSync(protoPath);
  for (const protoName of protoFileList) {
    //加载每个proto文件
    const protoFilePath = path.join(protoPath, protoName);
    const stats = fs.statSync(protoFilePath);
    if (stats.isFile() && path.extname(protoName) === '.proto') {
      const protoObj = await protoLoader.load(protoFilePath, config.loaderOption || {});
      const packageObj = GrpcCore.loadPackageDefinition(protoObj);
      for (const rpcpackage in protoObj) {
        // if (!rpcpackage.endsWith('Service')) continue;
        const protoServer = protoObj[rpcpackage];
        if (typeof packageObj[rpcpackage] !== 'function') continue;
        if (!protoServer) continue;
        const servicePackageClass = loadServie(rpcpackage);
        if (!servicePackageClass) continue;
        const service = buildService(protoServer, servicePackageClass);
        console.log(`[saasjs-grpc-server] ${rpcpackage} init`);
        saas.grpcServer.addService(protoServer, service);
        const client = await nacosClient();
        await client.registerInstance(rpcpackage, {ip: saas.config.host, port: config.port});
        console.log(`[saasjs-grpc-server] ${rpcpackage} nacos register`);
      }
    }
  }
}

function loadServie(serviceName) {
  serviceName = serviceName.replace('.', '/');
  const servicePath = path.resolve(saas.ROOT_PATH + '/' + saas.config.grpcServer.servicePath + '/' + serviceName);
  if (!fs.existsSync(servicePath + '.js')) {
    return null;
  }
  const Ctl = require(servicePath);
  return Ctl;
}


function buildService(protoServer, controllerClass) {
  const service = {};
  const handler = new controllerClass();
  // console.log(handler);
  for (const p in protoServer) {
    service[p] = (call, callback) => {
      const handlerMethod = handler[p];
      if (handlerMethod) {
        handler[p](call)
          .then((data) => {
            callback(null, data);
          })
          .catch((error) => {
            callback(error)
          })
      } else {
        callback(new Error('not implement'));
      }
    }
  }
  return service;
}